from abc import ABC, abstractmethod
from typing import Callable, List

from chernc.treediff.gumtree.tree.tree import TreeLike


class PriorityTreeQueue(ABC):
    HEIGHT_PRIORITY_CALCULATOR: Callable[[TreeLike], int] = lambda t: t.get_metrics().height
    SIZE_PRIORITY_CALCULATOR: Callable[[TreeLike], int] = lambda t: t.get_metrics().size

    @staticmethod
    def get_priority_calculator(name: str) -> Callable[[TreeLike], int]:
        if name == "size":
            return PriorityTreeQueue.SIZE_PRIORITY_CALCULATOR
        elif name == "height":
            return PriorityTreeQueue.HEIGHT_PRIORITY_CALCULATOR
        else:
            return PriorityTreeQueue.HEIGHT_PRIORITY_CALCULATOR

    @abstractmethod
    def pop_open(self) -> List[TreeLike]:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def set_priority_calculator(self, calculator: Callable[[TreeLike], int]) -> None:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def pop(self) -> List[TreeLike]:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def open(self, tree: TreeLike) -> None:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def current_priority(self) -> int:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def set_minimum_priority(self, priority: int) -> None:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def get_minimum_priority(self) -> int:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def is_empty(self) -> bool:
        raise NotImplementedError("This method must be implemented by the subclass")

    @abstractmethod
    def clear(self) -> None:
        raise NotImplementedError("This method must be implemented by the subclass")

    @staticmethod
    def synchronize(q1: 'PriorityTreeQueue', q2: 'PriorityTreeQueue') -> bool:
        while not (q1.is_empty() or q2.is_empty()) and q1.current_priority() != q2.current_priority():
            if q1.current_priority() > q2.current_priority():
                q1.pop_open()
            else:
                q2.pop_open()

        if q1.is_empty() or q2.is_empty():
            q1.clear()
            q2.clear()
            return False
        else:
            return True
